<?php
namespace app\common\services;

use think\facade\Config;
use Redis;
use Exception;
use think\facade\Env;

class RedisService 
{
    private static $instance = null;
    private $redis;

    // 构造函数私有化，防止直接创建实例
    private function __construct()
    {
        $this->redis = new \Redis();
        try {
            $config = config('app.redis');
            $this->redis->connect($config['redis_host'], $config['redis_port'], '60');
            if (!empty($config['redis_pass'])) {
                $this->redis->auth($config['redis_pass']);
            }
            if (!empty($config['redis_index'])) {
                $this->redis->select($config['redis_index']);
            }
        } catch (Exception $e) {
            throw new Exception("Redis 连接失败: " . $e->getMessage());
        }
    }

    // 获取 RedisService 实例
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    // 禁止克隆实例
    private function __clone() {}

    // 获取 Redis 实例
    public function getRedis()
    {
        return $this->redis;
    }

    // 设置 key 的过期时间（秒）
    public function expire($key, $ttl)
    {
        return $this->redis->expire($key, $ttl);
    }

    // 示例方法：设置键值
    public function set($key, $value, $expire = 0)
    {
        $this->redis->set($key, $value);
        if ($expire > 0) {
            $this->redis->expire($key, $expire);
        }
    }

    public function  exists($key){
        return $this->redis->get($key) ? true : false;
    }

   
    public function withLock(string $key, int $ttl, callable $callback)
    {
        if (!$this->acquireLock($key, $ttl)) {
            throw new \Exception('请勿重复操作');
        }

        try {
            return $callback(); // 执行业务逻辑
        } finally {
            $this->releaseLock($key); // 无论成功失败都释放锁
        }
    }

    // 获取锁
    public function acquireLock(string $key, int $ttl = 5): bool
    {
        return $this->redis->set($key, 1, ['nx', 'ex' => $ttl]);
    }

    // 释放锁
    public function releaseLock(string $key): bool
    {
        return $this->redis->del($key) > 0;
    }




    // 示例方法：获取键值
    public function get($key)
    {
        return $this->redis->get($key);
    }

    // 如果键不存在则设置键值
    public function setnx($key, $value)
    {
        return $this->redis->setnx($key, $value);
    }

    // List 操作：向左边推入列表
    public function lPush($key, $value)
    {
        return $this->redis->lPush($key, $value);
    }

    // List 操作：向右边推入列表
    public function rPush($key, $value)
    {
        return $this->redis->rPush($key, $value);
    }

    // List 操作：从左边弹出一个元素
    public function lPop($key)
    {
        return $this->redis->lPop($key);
    }

    // List 操作：从右边弹出一个元素
    public function rPop($key)
    {
        return $this->redis->rPop($key);
    }

    // List 操作：获取列表长度
    public function lLen($key)
    {
        return $this->redis->lLen($key);
    }



    // List 操作：获取列表中指定范围的元素
    public function lRange($key, $start, $stop)
    {
        return $this->redis->lRange($key, $start, $stop);
    }

    public function delete($key)
    {
        return $this->redis->del($key);
    }

        /**
     * Redis 锁机制
     * @param string $lock 锁的键名
     * @param mixed $param 传入的参数，用于回调函数
     * @param Closure $func 执行的回调函数
     * @param int $ttl 锁的有效期，单位为秒
     * @return mixed 回调函数的执行结果
     * @throws Exception
     */
    public function lock($lock, $param, \Closure $func, $ttl = 3)
    {
        // 尝试获取锁
        $isLocked = $this->redis->setnx($lock, 1);

        if (!$isLocked) {
            throw new \Exception('请求过快，请稍后再试哦~');
        }

        // 设置锁的过期时间，防止死锁
        $this->redis->expire($lock, $ttl);

        try {
            // 执行回调函数并传入参数和 Redis 实例
            $result = $func($param, $this->redis);
        } finally {
            // 释放锁，确保在回调执行完后删除锁
            $this->redis->del($lock);
        }

        return $result;
    }
}
